﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TuMama
{
    /* Adapted from code found here:
    * http://www.vbaccelerator.com/home/VB/Code/vbMedia/Audio/Reading_and_Writing_MP3_ID3v1_and_v2_Tags/article.asp
    *
    * By Penagate, 26 December 2005.
    *
    * Free to use/modify for any purpose, but please link to the original post:
    * http://www.vbforums.com/showthread.php?t=378193
    * rather than distributing this source file itself.
    */

    using System.IO;
    using System.Text;

    /// <summary>
    /// Holds ID3v1 tag information.
    /// Supports ID3 1.0-1.1.
    /// </summary>
    public class ID3v1tag
    {
        private struct mp3ID3v1tag
        {
            public byte[] tag;
            public byte[] title;
            public byte[] artist;
            public byte[] album;
            public byte[] year;
            public byte[] comment;
            // [version 1.1]
            public byte filler;
            public int track;
            // [/version 1.1]
            public int genre;

            public mp3ID3v1tag(bool useThisCtor)
            {
                tag = new byte[3];
                title = new byte[30];
                artist = new byte[30];
                album = new byte[30];
                year = new byte[4];
                comment = new byte[28];
                filler = 0;
                track = 0;
                genre = 0;
            }
        }

        private string _sMp3File;
        private bool _bHasID3v1Tag;

        public string
        Title,
        Artist,
        Album,
        Year,
        Comment;

        public int
        Genre,
        Track;

        public string MP3File
        {
            get
            {
                return _sMp3File;
            }
            set
            {
                _sMp3File = value;
                pLoadTag();
            }
        }

        public bool HasID3v1Tag
        {
            get
            {
                return _bHasID3v1Tag;
            }
        }

        /// <summary>
        /// Creates a new ID3v1 tag reader class and reads a tag from the specified file.
        /// </summary>
        /// <param name="Filename"></param>
        public ID3v1tag(string Filename)
        {
            MP3File = Filename;
        }

        public void Update()
        {
            pUpdateTag();
        }

        public string GenreName(byte Genre)
        {
            switch (Genre)
            {
                case 34: return "Acid";
                case 74: return "Acid Jazz";
                case 73: return "Acid Punk";
                case 99: return "Acoustic";
                case 40: return "Alt. Rock";
                case 20: return "Alternative";
                case 26: return "Ambient";
                case 145: return "Anime";
                case 90: return "Avant Garde";
                case 116: return "Ballad";
                case 41: return "Bass";
                case 135: return "Beat";
                case 85: return "Bebob";
                case 96: return "Big Band";
                case 138: return "Black Metal";
                case 89: return "Blue Grass";
                case 0: return "Blues";
                case 107: return "Booty Bass";
                case 132: return "Brit Pop";
                case 65: return "Cabaret";
                case 88: return "Celtic";
                case 104: return "Chamber Music";
                case 102: return "Chanson";
                case 97: return "Chorus";
                case 136: return "Christian Gangsta Rap";
                case 61: return "Christian Rap";
                case 141: return "Christian Rock";
                case 1: return "Classic Rock";
                case 32: return "Classical";
                case 112: return "Club";
                case 128: return "Club - House";
                case 57: return "Comedy";
                case 140: return "Contemporary Christian";
                case 2: return "Country";
                case 139: return "Crossover";
                case 58: return "Cult";
                case 3: return "Dance";
                case 125: return "Dance Hall";
                case 50: return "Darkwave";
                case 22: return "Death Metal";
                case 4: return "Disco";
                case 55: return "Dream";
                case 127: return "Drum & Bass";
                case 122: return "Drum Solo";
                case 120: return "Duet";
                case 98: return "Easy Listening";
                case 52: return "Electronic";
                case 48: return "Ethnic";
                case 54: return "Eurodance";
                case 124: return "Euro - House";
                case 25: return "Euro - Techno";
                case 84: return "Fast Fusion";
                case 80: return "Folk";
                case 81: return "Folk / Rock";
                case 115: return "Folklore";
                case 119: return "Freestyle";
                case 5: return "Funk";
                case 30: return "Fusion";
                case 36: return "Game";
                case 59: return "Gangsta Rap";
                case 126: return "Goa";
                case 38: return "Gospel";
                case 49: return "Gothic";
                case 91: return "Gothic Rock";
                case 6: return "Grunge";
                case 79: return "Hard Rock";
                case 129: return "Hardcore";
                case 137: return "Heavy Metal";
                case 7: return "Hip Hop";
                case 35: return "House";
                case 100: return "Humour";
                case 131: return "Indie";
                case 19: return "Industrial";
                case 33: return "Instrumental";
                case 46: return "Instrumental Pop";
                case 47: return "Instrumental Rock";
                case 8: return "Jazz";
                case 29: return "Jazz - Funk";
                case 146: return "JPop";
                case 63: return "Jungle";
                case 86: return "Latin";
                case 71: return "Lo - fi";
                case 45: return "Meditative";
                case 142: return "Merengue";
                case 9: return "Metal";
                case 77: return "Musical";
                case 82: return "National Folk";
                case 64: return "Native American";
                case 133: return "Negerpunk";
                case 10: return "New Age";
                case 66: return "New Wave";
                case 39: return "Noise";
                case 11: return "Oldies";
                case 103: return "Opera";
                case 12: return "Other";
                case 75: return "Polka";
                case 134: return "Polsk Punk";
                case 13: return "Pop";
                case 62: return "Pop/Funk";
                case 53: return "Pop/Folk";
                case 109: return "Porn Groove";
                case 117: return "Power Ballad";
                case 23: return "Pranks";
                case 108: return "Primus";
                case 92: return "Progressive Rock";
                case 67: return "Psychedelic";
                case 93: return "Psychedelic Rock";
                case 43: return "Punk";
                case 121: return "Punk Rock";
                case 14: return "R&B";
                case 15: return "Rap";
                case 68: return "Rave";
                case 16: return "Reggae";
                case 76: return "Retro";
                case 87: return "Revival";
                case 118: return "Rhythmic Soul";
                case 17: return "Rock";
                case 78: return "Rock'n'Roll";
                case 143: return "Salsa";
                case 114: return "Samba";
                case 110: return "Satire";
                case 69: return "Showtunes";
                case 21: return "Ska";
                case 111: return "Slow Jam";
                case 95: return "Slow Rock";
                case 105: return "Sonata";
                case 42: return "Soul";
                case 37: return "Sound Clip";
                case 24: return "Soundtrack";
                case 56: return "Southern Rock";
                case 44: return "Space";
                case 101: return "Speech";
                case 83: return "Swing";
                case 94: return "Symphonic Rock";
                case 106: return "Symphony";
                case 147: return "Synth Pop";
                case 113: return "Tango";
                case 18: return "Techno";
                case 51: return "Techno - Industrial";
                case 130: return "Terror";
                case 144: return "Thrash Metal";
                case 60: return "Top 40";
                case 70: return "Trailer";
                case 31: return "Trance";
                case 72: return "Tribal";
                case 27: return "Trip Hop";
                case 28: return "Vocal";
            }
            return "Unknown";
        }

        private string trimAll(string text)
        {
            return text.Contains("\0") ? text.Substring(0, text.IndexOf('\0')) : text.Trim();
        }

        private void pLoadTag()
        {
            _bHasID3v1Tag = false;
            Comment = string.Empty;
            Artist = string.Empty;
            Album = string.Empty;
            Year = string.Empty;
            Genre = 255;
            Title = string.Empty;
            Track = 0;

            if (File.Exists(_sMp3File))
            {
                FileStream fs = new FileStream(_sMp3File, FileMode.Open);

                if (fs.Length > 128)
                {
                    mp3ID3v1tag Tag = new mp3ID3v1tag(true);

                    fs.Seek(-128, SeekOrigin.End);
                    fs.Read(Tag.tag, 0, 3);

                    ASCIIEncoding encoding = new ASCIIEncoding();
                    if (encoding.GetString(Tag.tag).ToUpper() == "TAG")
                    {
                        _bHasID3v1Tag = true;

                        fs.Read(Tag.title, 0, 30);
                        Title = trimAll(encoding.GetString(Tag.title));
                        fs.Read(Tag.artist, 0, 30);
                        Artist = trimAll(encoding.GetString(Tag.artist));
                        fs.Read(Tag.album, 0, 30);
                        Album = trimAll(encoding.GetString(Tag.album));
                        fs.Read(Tag.year, 0, 4);
                        Year = trimAll(encoding.GetString(Tag.year));
                        fs.Read(Tag.comment, 0, 28);
                        Comment = trimAll(encoding.GetString(Tag.comment));
                        // [version 1.1]
                        fs.ReadByte(); // Ignore the filler byte
                        Tag.track = fs.ReadByte();
                        // [/version 1.1]
                        Tag.genre = fs.ReadByte();
                        Genre = Tag.genre;
                    }
                }

                fs.Close();
            }
        }

        private byte[] strToByteArr(Encoding encoding, string text, int maxChars)
        {
            if (text.Length > maxChars)
                text = text.Substring(0, maxChars);
            else
                if (text.Length < maxChars)
                    text = text.PadRight(maxChars, '\0');

            return encoding.GetBytes(text);
        }

        private void pUpdateTag()
        {
            if (File.Exists(MP3File))
            {
                FileStream fs = new FileStream(MP3File, FileMode.Open);
                mp3ID3v1tag tag = new mp3ID3v1tag(true);
                ASCIIEncoding encoding = new ASCIIEncoding();

                if (fs.Length > 128)
                {
                    fs.Seek(-127, SeekOrigin.End);
                    fs.Read(tag.tag, 0, 3);
                    if (!(tag.tag.ToString().ToUpper() == "TAG"))
                    {
                        /* no MP3 tag already, need to extend the file
                        to add it */
                        fs.Seek(0, SeekOrigin.End);
                        tag.tag = strToByteArr(encoding, "TAG", 3);
                        fs.Write(tag.tag, 0, 3);
                    }

                    tag.title = strToByteArr(encoding, Title, 30);
                    tag.artist = strToByteArr(encoding, Artist, 30);
                    tag.album = strToByteArr(encoding, Album, 30);
                    tag.year = strToByteArr(encoding, Year, 4);
                    // [version 1.1]
                    tag.track = Track;
                    // [/version 1.1]
                    tag.comment = strToByteArr(encoding, Comment, 28);
                    tag.genre = Genre;

                    fs.Write(tag.title, 0, 30);
                    fs.Write(tag.artist, 0, 30);
                    fs.Write(tag.album, 0, 30);
                    fs.Write(tag.year, 0, 4);
                    fs.Write(tag.comment, 0, 28);
                    // [version 1.1]
                    fs.WriteByte(0);
                    fs.WriteByte((byte)tag.track);
                    // [/version 1.1]
                    fs.WriteByte((byte)tag.genre);
                }

                fs.Close();
            }
        }
    }
}
